﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ShopSpace.Core.Query;

namespace ShopSpace.Core.Specification
{
	public interface ISpecification<T>
	{
		bool IsSatisfiedBy(T candidate);
		ISpecification<T> And(ISpecification<T> other);
		ISpecification<T> Or(ISpecification<T> other);
		ISpecification<T> Not();
		QueryBuilder<T> AsQueryBuilder();
		
	}

	public abstract class CompositeSpecification<T> : ISpecification<T>
	{
		public abstract bool IsSatisfiedBy(T candidate);
		public abstract QueryBuilder<T> AsQueryBuilder();

		public ISpecification<T> And(ISpecification<T> other)
		{
			return new AndSpecification<T>(this, other);
		}

		public ISpecification<T> Or(ISpecification<T> other)
		{
			return new OrSpecification<T>(this, other);
		}

		public ISpecification<T> Not()
		{
			return new NotSpecification<T>(this);
		}
	}

	public class AndSpecification<T> : CompositeSpecification<T>
	{
		private ISpecification<T> one;
		private ISpecification<T> other;

		public AndSpecification(ISpecification<T> x, ISpecification<T> y)
		{
			one = x;
			other = y;
		}

		public override bool IsSatisfiedBy(T candidate)
		{
			return one.IsSatisfiedBy(candidate) && other.IsSatisfiedBy(candidate);
		}

		public override QueryBuilder<T> AsQueryBuilder()
		{
			return one.AsQueryBuilder() && other.AsQueryBuilder();
		}
	}

	public class OrSpecification<T> : CompositeSpecification<T>
	{
		private ISpecification<T> one;
		private ISpecification<T> other;

		public OrSpecification(ISpecification<T> x, ISpecification<T> y)
		{
			one = x;
			other = y;
		}

		public override bool IsSatisfiedBy(T candidate)
		{
			return one.IsSatisfiedBy(candidate) || other.IsSatisfiedBy(candidate);
		}

		public override QueryBuilder<T> AsQueryBuilder()
		{
			return one.AsQueryBuilder() || other.AsQueryBuilder();
		}
	}

	public class NotSpecification<T> : CompositeSpecification<T>
	{
		private ISpecification<T> wrapped;

		public NotSpecification(ISpecification<T> x)
		{
			wrapped = x;
		}

		public override bool IsSatisfiedBy(T candidate)
		{
			return !wrapped.IsSatisfiedBy(candidate);
		}

		public override QueryBuilder<T> AsQueryBuilder()
		{
			return !wrapped.AsQueryBuilder();
		}
	}
}